package synapsolution;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;

public class Population extends ArrayList<Network> {

    final static int capacity = 1000;
    final static double crossover = .9;
    final static double copy = .2;
    final static Random rng = new Random();
    public static final boolean showGenerations = true;
    public static final boolean showCombinations = false;
    public static final boolean showComparisons = false;
    public static final boolean showBest = true;

    public static void main(String[] args) {
        int maxGenerations = 1000;
        Population current = new Population();
        for (int i = 0; i < capacity; i++) current.add(new PrimeNetwork());
        System.out.println(capacity + " neural networks created.");
        for (int i = 0; i < maxGenerations; i++) {
            current = current.generate();
            if (showGenerations) System.out.println("** Generation " + i + " -- " + current.toString());
        }
    }

    public Population() {
    }

    private Population(Network[] array) {
        addAll(Arrays.asList(array));
    }

    public Population generate() {
        Network[] sorted = toArray(new Network[size()]);
        Arrays.sort(sorted);
        Population now = new Population(sorted);
        Population next = new Population();
        while (next.size() < capacity) {
            Network selection = now.get(randomSmall(now.size() - 1));
            if (!next.contains(selection) && rng.nextDouble() < copy) next.add(selection);
            if (next.size() < capacity && rng.nextDouble() < crossover)
                next.add(new PrimeNetwork((PrimeNetwork) selection, (PrimeNetwork) now.get(randomSmall(now.size() - 1))));
        }
        return next;
    }

    static int randomSmall(int max) {
        return (int) (Math.pow(rng.nextDouble(), 10) * max);
    }

    @Override
    public String toString() {
        return "Worst: " + maxFitness() + ", Average: " + averageFitness() + ". Best: " + minFitness();
    }

    private double averageFitness() {
        int avgfitness = 0;
        for (Network n : this) avgfitness += n.getFitness();
        return (double) avgfitness / size();
    }

    private int minFitness() {
        int min = Integer.MAX_VALUE;
        Network best = null;
        for (Network n : this) if (n.getFitness() < min) {
                best = n;
                min = n.getFitness();
            }
        if (showBest) System.out.println("Displaying the best network in this population...\n" + best);
        return min;
    }

    private int maxFitness() {
        int max = Integer.MIN_VALUE;
        for (Network n : this) if (n.getFitness() > max) max = n.getFitness();
        return max;
    }
}
